package com.black.utils.file.image.code;

import com.black.utils.file.FileTypeEnum;
import com.black.utils.file.FileUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ylx
 */
@Slf4j
public class QRCodeUtil {
    /**
     * Creates an image file containing a QR code with the given content.
     * 创建一个包含给定内容的二维码的图片文件。
     *
     * @param filePath The path where the image file will be saved.
     *                 图片文件保存的路径。
     * @param fileName The name of the image file.
     *                 图片文件的名称。
     * @param content  The content to encode in the QR code.
     *                 要编码到二维码中的内容。
     * @return True if the image file was successfully created, false otherwise.
     * 如果图片文件创建成功，则返回true，否则返回false。
     */
    public static boolean createImage(String filePath, String fileName, String content) {
        try {
            byte[] bytes = createQrCode(content);
            return FileUtil.saveBytes(filePath, fileName, bytes);
        } catch (Exception e) {
            log.error("create qr code error", e);
        }
        return false;
    }

    /**
     * Creates an image file containing a QR code with the given content and specified width.
     * 创建一个包含给定内容和指定宽度的二维码的图片文件。
     *
     * @param filePath The path where the image file will be saved.
     *                 图片文件保存的路径。
     * @param fileName The name of the image file.
     *                 图片文件的名称。
     * @param content  The content to encode in the QR code.
     *                 要编码到二维码中的内容。
     * @param width    The width of the QR code.
     *                 二维码的宽度。
     * @return True if the image file was successfully created, false otherwise.
     * 如果图片文件创建成功，则返回true，否则返回false。
     */
    public static boolean createImage(String filePath, String fileName, String content, int width) {
        try {
            byte[] bytes = createQrCode(content, width);
            return FileUtil.saveBytes(filePath, fileName, bytes);
        } catch (Exception e) {
            log.error("create qr code error", e);
        }
        return false;
    }

    public static int DEFAULT_WIDTH = 240;


    /**
     * Creates a QR code with the given content and default width.
     * 使用给定的内容和默认宽度创建二维码。
     *
     * @param content The content to encode in the QR code.
     *                要编码到二维码中的内容。
     * @return A byte array representing the QR code image.
     * 表示二维码图像的字节数组。
     * @throws Exception If an error occurs during QR code creation.
     *                   如果在创建二维码过程中发生错误，则抛出异常。
     */
    public static byte[] createQrCode(String content) throws Exception {
        return createQrCode(content, DEFAULT_WIDTH, false);
    }

    /**
     * Creates a QR code with the given content and the option to write text with the default width.
     * 使用给定的内容和选项创建带有默认宽度的二维码，可选择是否写入文本。
     *
     * @param content   The content to encode in the QR code.
     *                  要编码到二维码中的内容。
     * @param writeText A boolean indicating whether to write text on the QR code.
     *                  一个布尔值，指示是否在二维码上写入文本。
     * @return A byte array representing the QR code image.
     * 表示二维码图像的字节数组。
     * @throws Exception If an error occurs during QR code creation.
     *                   如果在创建二维码过程中发生错误，则抛出异常。
     */
    public static byte[] createQrCode(String content, boolean writeText) throws Exception {
        return createQrCode(content, DEFAULT_WIDTH, writeText);
    }

    /**
     * Creates a QR code with the given content and the specified width, without writing text.
     * 使用给定的内容和指定的宽度创建二维码，不写入文本。
     *
     * @param content The content to encode in the QR code.
     *                要编码到二维码中的内容。
     * @param width   The width of the QR code image.
     *                二维码图像的宽度。
     * @return A byte array representing the QR code image.
     * 表示二维码图像的字节数组。
     * @throws Exception If an error occurs during QR code creation.
     *                   如果在创建二维码过程中发生错误，则抛出异常。
     */
    public static byte[] createQrCode(String content, Integer width) throws Exception {
        return createQrCode(content, width, false);
    }

    /**
     * Creates a QR code image with the given content, width, and optional text writing.
     * 使用给定的内容、宽度和是否写入文本创建二维码图像。
     *
     * @param content   The content to encode in the QR code.
     *                  要编码到二维码中的内容。
     * @param width     The width and height of the QR code image (square dimensions).
     *                  二维码图像的宽度和高度（正方形尺寸）。
     * @param writeText Whether to write the content text below the QR code image.
     *                  是否在二维码图像下方写入内容文本。
     * @return A byte array representing the QR code image.
     * 表示二维码图像的字节数组。
     * @throws Exception If an error occurs during QR code creation.
     *                   如果在创建二维码过程中发生错误，则抛出异常。
     */
    public static byte[] createQrCode(String content, Integer width, boolean writeText) throws Exception {
        log.info("start create qr code");
        Map<EncodeHintType, Object> hints = new HashMap<>();
        // 指定编码格式
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        // 指定纠错级别(L--7%,M--15%,Q--25%,H--30%)
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 编码内容,编码类型(这里指定为二维码),生成图片宽度,生成图片高度,设置参数
        int fontSize = 24;
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, width, hints);
        //创建 BufferedImage 对象，并设置背景色为白色
        int height = writeText ? width + fontSize : width;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = image.createGraphics();
        // 画笔填充白色背景框
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);
        // 绘制二维码
        graphics.setColor(Color.BLACK);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < width; y++) {
                if (bitMatrix.get(x, y)) {
                    graphics.fillRect(x, y, 1, 1);
                }
            }
        }
        if (writeText) {
            // 在二维码下方绘制文字
            graphics.setFont(new Font("Arial", Font.PLAIN, fontSize));
            FontMetrics fontMetrics = graphics.getFontMetrics();
            int numberWidth = fontMetrics.stringWidth(content);
            int numberX = (width - numberWidth) / 2;
            int numberY = width + fontSize / 2;
            graphics.drawString(content, numberX, numberY);
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(image, FileTypeEnum.PNG.getExtension(), out);
        byte[] qrCodeByte = out.toByteArray();
        log.info("create qr code over");
        return qrCodeByte;
    }
}
